/**
 * pecker.c - Main WoodPecker Plugin source.
 *
 * WoodPecker
 *
 * Copyright (C) 2003, Robbert Haarman <purple@inglorion.net>
 * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu>
 * Copyright (C) 2000-2003, Rob Flynn <rob@tgflinux.com>
 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
 * Copyright (C) 2008, The WoodPecker Team
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: pecker.c 128 2008-04-13 01:34:40Z dubkat $
 */

#include "accountopt.h"
#include "blist.h"
#include "conversation.h"
#include "debug.h"
#include "notify.h"
#include "prpl.h"
#include "plugin.h"
#include "util.h"
#include "version.h"
#include "request.h"
#include "privacy.h"
#include "signals.h"

#include "helpers.h"
#include "info.h"
#include "priv.h"
#include "pecker.h"

static const char *pecker_blist_icon(PurpleAccount * a, PurpleBuddy * b);
static GList *pecker_status_types(PurpleAccount * account);
static GList *pecker_actions(PurplePlugin * plugin, gpointer context);

/* static GList *pecker_chat_info(PurpleConnection *gc); */
static void pecker_login(PurpleAccount * account);
static void pecker_login_cb(gpointer data, gint source, const gchar * error_message);
static void pecker_close(PurpleConnection * gc);
static int pecker_im_send(PurpleConnection * gc, const char *who, const char *what, PurpleMessageFlags flags);
static int pecker_chat_send(PurpleConnection * gc, int id, const char *what, PurpleMessageFlags flags);
static void pecker_chat_join(PurpleConnection * gc, GHashTable * data);
static void pecker_input_cb(gpointer data, gint source, PurpleInputCondition cond);
static guint pecker_nick_hash(const char *nick);
static gboolean pecker_nick_equal(const char *nick1, const char *nick2);
static void pecker_buddy_free(struct pecker_buddy *ib);
static void pecker_channel_member_free(PeckerBuddy * cm);

static void pecker_buddy_append(char *name, struct pecker_buddy *ib, BListWhois * blist_whois);
static void pecker_buddy_clear_done(char *name, struct pecker_buddy *ib, gpointer nothing);

static void connect_signals(PurpleConnection * plugin);
static PurplePlugin *_pecker_plugin = NULL;

static const char *status_chars = "@+%&";

int
pecker_send(struct pecker_conn *pecker, const char *buf)
{
    int ret;

    if(pecker->fd < 0)
        return -1;

    purple_debug(PURPLE_DEBUG_MISC, "pecker", "sent: %s", buf);
    if((ret = write(pecker->fd, buf, strlen(buf))) < 0)
        purple_connection_error(purple_account_get_connection(pecker->account), "Server has disconnected");

    return ret;
}

gboolean
pecker_blist_timeout(struct pecker_conn * pecker)
{
    /*
     * There are 510 characters available for an IRC command (512 if
     * you count CR-LF).  "WHOIS " takes up 6 characters.  Assuming
     * you need allow an extra character for the NULL when using
     * g_string_sized_new(), we need to allocate (510-6)+1=505 here.
     */
    BListWhois *blist_whois = g_new0(BListWhois, 1);

    blist_whois->count = 0;
    blist_whois->string = g_string_sized_new(505);

    char *list, *buf;

    g_hash_table_foreach(pecker->buddies, (GHFunc) pecker_buddy_append, (gpointer) blist_whois);

    list = g_string_free(blist_whois->string, FALSE);
    if(!list || !strlen(list))
    {
        g_hash_table_foreach(pecker->buddies, (GHFunc) pecker_buddy_clear_done, NULL);
        purple_timeout_remove(pecker->timer);
        pecker->timer = purple_timeout_add(BLIST_UPDATE_PERIOD, (GSourceFunc) pecker_blist_timeout, (gpointer) pecker);
        g_free(list);
        g_free(blist_whois);

        return TRUE;
    }
    pecker->blist_updating = TRUE;
    buf = pecker_format(pecker, "vn", "WHOIS", list);
    pecker_send(pecker, buf);
    purple_timeout_remove(pecker->timer);
    pecker->timer = purple_timeout_add(BLIST_CHUNK_INTERVAL, (GSourceFunc) pecker_blist_timeout, (gpointer) pecker);

    g_free(buf);
    g_free(list);
    g_free(blist_whois);

    return TRUE;
}

static void
pecker_buddy_clear_done(char *name, struct pecker_buddy *ib, gpointer nothing)
{
    ib->done = FALSE;
}

static void
pecker_buddy_append(char *name, struct pecker_buddy *ib, BListWhois * blist_whois)
{
    char *converted_name = NULL;

    converted_name = pecker_nick_to_gcom_strdup(name);
    /*
     * 
     * There are 510 characters available for an IRC command (512 if
     * you count CR-LF).  "WHOIS " takes up 6 characters.  This means
     * we have up to 504 characters available for comma separated
     * converted_names
     */
    if(ib->done == FALSE && blist_whois->count < 10 && (strlen(converted_name) + blist_whois->string->len + 1) <= 504)
    {
        blist_whois->count++;
        ib->done = TRUE;
        if(blist_whois->string->len == 0)
        {
            g_string_append_printf(blist_whois->string, "%s", converted_name);
        }
        else
        {
            g_string_append_printf(blist_whois->string, ",%s", converted_name);
        }
    }

    g_free(converted_name);
    return;
}

static void
pecker_whois_one(struct pecker_conn *pecker, struct pecker_buddy *ib)
{
    char *buf;
    char *nick;

    nick = pecker_nick_to_gcom_strdup(ib->name);
    buf = pecker_format(pecker, "vn", "WHOIS", nick);
    pecker_send(pecker, buf);
    g_free(nick);
    g_free(buf);
}

static const char *
pecker_blist_icon(PurpleAccount * a, PurpleBuddy * b)
{
    return "pecker";
}

static char *
pecker_status_text(PurpleBuddy * buddy)
{
    char *status;

    struct pecker_conn *pecker = (struct pecker_conn *) buddy->account->gc->proto_data;

    if(!pecker)
    {
        return g_strdup("Offline");
    }

    struct pecker_buddy *ib = g_hash_table_lookup(pecker->buddies, buddy->name);

    if(!ib)
    {
        return g_strdup("Offline");
    }

    if(!ib->online)
    {
        return g_strdup("Offline");
    }

    if(!ib->bio)
    {
        return NULL;
    }

    status = g_markup_escape_text(ib->bio, strlen(ib->bio));

    return status;
}

static void
pecker_tooltip_text(PurpleBuddy * buddy, PurpleNotifyUserInfo * user_info, gboolean full)
{
    if(!buddy || !buddy->account || !buddy->account->gc)
        return;


    struct pecker_conn *pecker = (struct pecker_conn *) buddy->account->gc->proto_data;

    if(!pecker)
    {
        return;
    }

    struct pecker_buddy *ib = g_hash_table_lookup(pecker->channel_members,
                                                  purple_normalize(pecker->account,
                                                                   buddy->name));

    if(!ib)
        ib = g_hash_table_lookup(pecker->buddies, purple_normalize(pecker->account, buddy->name));

    build_tooltip_text(ib, user_info);

}

static GList *
pecker_status_types(PurpleAccount * account)
{
    PurpleStatusType *type;
    GList *types = NULL;

    type = purple_status_type_new(PURPLE_STATUS_OFFLINE, "offline", "Offline", FALSE);
    types = g_list_append(types, type);

    type = purple_status_type_new(PURPLE_STATUS_AVAILABLE, "available", "Available", TRUE);
    types = g_list_append(types, type);

    type =
        purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, "away", "Away", TRUE, TRUE, FALSE, "message", "Message",
                                          purple_value_new(PURPLE_TYPE_STRING), NULL);
    types = g_list_append(types, type);

    return types;

}

static void
pecker_set_info(PurpleConnection * gc, const char *info)
{
    struct pecker_conn *pecker = gc->proto_data;
    PurpleAccount *account = purple_connection_get_account(gc);
    char *hostname = "none";
    char *buf, *bioline;
    int i = 0;
    char *tmpinfo = NULL;

    if(info)
    {
        tmpinfo = g_strdup(info);
        for (i = 0; i < strlen(tmpinfo); i++)
        {
            if(tmpinfo[i] == '\n')
            {
                tmpinfo[i] = ' ';
            }
        }
        tmpinfo = g_strstrip(tmpinfo);
    }

    if(!tmpinfo)
    {
        /**
         * don't change any bio settings, since this is just
         * setting an away message
         */
    }
    else
    {
        if(pecker->bio)
        {
            g_free(pecker->bio);
        }
        if(tmpinfo && strlen(tmpinfo) > 0)
        {
            purple_debug_misc("pecker", "option1, info=%s\n", tmpinfo);
            /* java client allows MAX_BIO_LEN characters */
            pecker->bio = g_strndup(tmpinfo, MAX_BIO_LEN);
        }
        else if(pecker->server_bioline && strlen(pecker->server_bioline) > 0)
        {
            purple_debug_misc("pecker", "option2\n");
            pecker->bio = pecker_bio_strdup(pecker->server_bioline);
        }
        else
        {
            purple_debug_misc("pecker", "option3\n");
            pecker->bio = g_strdup("Purple User");
        }
        purple_account_set_user_info(account, pecker->bio);
        purple_account_set_string(account, "bioline", pecker->bio);
        purple_debug_info("pecker", "INFO=%s BIO=%s\n", tmpinfo, pecker->bio);
        purple_debug_misc("pecker", "In login_cb, gc->account=%s\n", ((const char *) gc->account));
    }

    bioline = g_strdup_printf("%s#%s\001%s", pecker->thumbnail ? pecker->thumbnail : "",
                              // gc->away ? gc->away : (pecker->bio ? pecker->bio :
                              // ""),
                              (pecker->bio ? pecker->bio : ""), pecker->server_stats ? pecker->server_stats : "");

    buf = pecker_format(pecker, "vvvv:", "USER", purple_account_get_username(account), hostname, pecker->server, bioline);

    purple_debug_misc("pecker", "BIO=%s\n", bioline);

    if(pecker_send(pecker, buf) < 0)
    {
        purple_connection_error(gc, "Error registering with server");
    }

    if(tmpinfo)
    {
        g_free(tmpinfo);
    }
    g_free(bioline);
    g_free(buf);

    return;
}

static void
pecker_show_set_info(PurplePluginAction * action)
{
    PurpleConnection *gc = (PurpleConnection *) action->context;

    purple_account_request_change_user_info(purple_connection_get_account(gc));
}

static GList *
pecker_actions(PurplePlugin * plugin, gpointer context)
{
    GList *list = NULL;
    PurplePluginAction *act = NULL;

    act = purple_plugin_action_new("Change Bio", pecker_show_set_info);
    list = g_list_prepend(list, act);

    return list;
}

static void
pecker_blist_join_chat_cb(PurpleBlistNode * node, gpointer data)
{
    const char *args[1];

    PurpleChat *chat = (PurpleChat *) node;
    struct pecker_conn *pecker = chat->account->gc->proto_data;

    args[0] = data;

    g_return_if_fail(args[0] != NULL);
    g_return_if_fail(pecker != NULL);

    pecker_cmd_join(pecker, "join", NULL, args);
}

static GList *
pecker_blist_node_menu(PurpleBlistNode * node)
{
    GList *m = NULL;
    PurpleMenuAction *act = NULL;
    int i = 0;

    if(node->type == PURPLE_BLIST_CHAT_NODE)
    {

        PurpleChat *chat = (PurpleChat *) node;
        char *channel = g_hash_table_lookup(chat->components, "channel");

        if(!channel)
        {
            return m;
        }

        if(!g_str_has_suffix(channel, "=*"))
        {
            return m;
        }

        char *label = NULL;
        char *instance = NULL;

        int max = purple_prefs_get_int("/plugins/prpl/pecker/chat_room_instances");

        for (i = max; i > 0; i--)
        {
            label = g_strdup_printf("Join Room %d", i);
            instance = g_strdup_printf("%.*s%d", ((unsigned int) strlen(channel) - 1), channel, i);
            act = purple_menu_action_new(label, PURPLE_CALLBACK(pecker_blist_join_chat_cb), instance, NULL);
            m = g_list_prepend(m, act);
        }
    }
    if(PURPLE_BLIST_NODE_IS_BUDDY(node))
    {

    }
    return m;
}

static GList *
pecker_chat_join_info(PurpleConnection * gc)
{
    GList *m = NULL;
    struct proto_chat_entry *pce;

    pce = g_new0(struct proto_chat_entry, 1);

    pce->label = "_Room:";
    pce->identifier = "channel";
    m = g_list_prepend(m, pce);

    return m;
}

GHashTable *
pecker_chat_info_defaults(PurpleConnection * gc, const char *chat_name)
{
    GHashTable *defaults;

    defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);

    if(chat_name != NULL)
        g_hash_table_insert(defaults, "channel", g_strdup(chat_name));

    return defaults;
}

static void
pecker_login_with_chat_key(PurpleAccount * account)
{
    PurpleConnection *gc;
    struct pecker_conn *pecker;
    char *buf;
    const char *username = purple_account_get_username(account);

    gc = purple_account_get_connection(account);
    pecker = gc->proto_data;

    buf = g_strdup_printf("Signon: %s", username);
    purple_connection_update_progress(gc, buf, 5, 6);
    g_free(buf);
    purple_debug_misc("pecker", "Trying login to %s\n", pecker->server);
    PurpleProxyConnectData *pdata = purple_proxy_connect(NULL, account,
                                                         pecker->server,
                                                         purple_account_get_int(account, "port",
                                                                                IRC_DEFAULT_PORT),
                                                         *pecker_login_cb,
                                                         gc);

    if(!pdata || !account->gc)
    {
        purple_connection_error(gc, "Couldn't create socket");
        purple_debug_misc("pecker", "account->gc: %s\n", ((const char *) account->gc));
        return;
    }

}

guint
pecker_room_hash(gconstpointer key)
{

    if(*((char *) key) == 0)
        return 0;

    return atoi((char *) (key + 1));


}

static void
pecker_login(PurpleAccount * account)
{
    PurpleConnection *gc;
    struct pecker_conn *pecker;
    char *buf;
    const char *username = purple_account_get_username(account);

    gc = purple_account_get_connection(account);
    gc->flags |= PURPLE_CONNECTION_NO_NEWLINES | PURPLE_CONNECTION_AUTO_RESP;
    gc->flags &= ~PURPLE_CONNECTION_HTML;

    if(strpbrk(username, " \t\v\r\n") != NULL)
    {
        purple_connection_error(gc, "IRC nicks may not contain whitespace");
        return;
    }

    gc->proto_data = pecker = g_new0(struct pecker_conn, 1);
    pecker->account = account;


    /* purple_connection_set_display_name(gc, userparts[0]); */
    purple_connection_set_display_name(gc, username);
    pecker->server = g_strdup(purple_account_get_string(account, "server", "www.gay.com"));
    /*  pecker->server = "www.gay.com";  */


    pecker->namelists = g_queue_new();

    pecker->buddies =
        g_hash_table_new_full((GHashFunc) pecker_nick_hash, (GEqualFunc) pecker_nick_equal, NULL,
                              (GDestroyNotify) pecker_buddy_free);

    pecker->channel_members =
        g_hash_table_new_full((GHashFunc) pecker_nick_hash, (GEqualFunc) pecker_nick_equal, NULL,
                              (GDestroyNotify) pecker_channel_member_free);

    pecker->cmds = g_hash_table_new(g_str_hash, g_str_equal);
    pecker_cmd_table_build(pecker);
    pecker->msgs = g_hash_table_new(g_str_hash, g_str_equal);
    pecker_msg_table_build(pecker);
    pecker->roomlist_filter = NULL;

    pecker->hammers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) hammer_cb_data_destroy);
    /*
     * The last parameter needs to be NULL here, since the same
     * field is added for both the key and the value (and if we
     * free it twice, thats bad and causes crashing!).
     */

    pecker->nameconv = NULL;

    pecker->info_window_needed = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);

    pecker->entry_order = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);

    /*
     * This is similar to pecker->info_window_needed, except this is
     * for thumbails inside the IM conversation window if the
     * person is not already on the buddy list
     */

    buf = g_strdup_printf("Signon: %s", username);
    purple_connection_update_progress(gc, buf, 1, 6);
    g_free(buf);


    /*
     * Making a change to try cached password first.
     * pecker_try_cached_password(account, pecker_login_with_chat_key);
     */
    pecker_get_chat_key_from_weblogin(account, pecker_login_with_chat_key);
}


static void
pecker_get_configtxt_cb(PurpleUtilFetchUrlData * data, gpointer proto_data, const gchar * config_text, size_t len,
                        const gchar * error_message)
{
    struct pecker_conn *pecker = (struct pecker_conn *) proto_data;

    // PurpleConnection *gc = purple_account_get_connection(pecker->account);

    g_return_if_fail(config_text != NULL);

    pecker->confighash = pecker_properties_new(config_text);
    g_return_if_fail(pecker->confighash != NULL);

    // if(roomlist=g_hash_table_lookup(pecker->confighash, "roomlist"))
    // pecker->roomlist = pecker_parse_roomlist();
    // synchronize_deny_list(gc, pecker->confighash);

    return;
}
static void
pecker_login_cb(gpointer data, gint source, const gchar * error_message)
{
    PurpleConnection *gc = data;
    struct pecker_conn *pecker = gc->proto_data;
    char hostname[256] = "WoodPecker";
    char *buf;
    const char *username;
    const char *user_bioline = NULL;
    char *bioline;
    char *login_name;

    if(PURPLE_CONNECTION_IS_VALID(gc))
    {


        GList *connections = purple_connections_get_all();

        if(source < 0)
        {
            purple_connection_error(gc, "Couldn't connect to host");
            return;
        }

        if(!g_list_find(connections, gc))
        {
            close(source);
            return;
        }

        pecker->fd = source;
        purple_debug_misc("pecker", "In login_cb with chat_key=%s\n", pecker->chat_key);
        if(pecker->chat_key)
        {

            buf = pecker_format(pecker, "vv", "PASS", pecker->chat_key);
            if(pecker_send(pecker, buf) < 0)
            {
                purple_connection_error(gc, "Error sending password");
                return;
            }
            g_free(buf);
        }
        else
        {
            purple_connection_error(gc, "Password wasn't recorded. Report bug.");
            return;
        }
        username = purple_account_get_string(pecker->account, "username", "");
        user_bioline = g_strdup(purple_account_get_string(pecker->account, "bioline", ""));
        purple_debug_info("pecker", "USER BIOLINE=%s\n", user_bioline);
        purple_account_set_user_info(gc->account, user_bioline);
        purple_debug_misc("pecker", "In login_cb, user_bioline: %s, gc->account=%s\n", user_bioline,
                          ((const char *) gc->account));

        login_name = pecker_nick_to_gcom_strdup(purple_connection_get_display_name(gc));
        bioline =
            g_strdup_printf("%s#%s\001%s", pecker->thumbnail, user_bioline ? user_bioline : "",
                            pecker->server_stats ? pecker->server_stats : "");

        buf = pecker_format(pecker, "vn", "NICK", login_name);
        purple_debug_misc("pecker", "Command: %s\n", buf);

        if(pecker_send(pecker, buf) < 0)
        {
            purple_connection_error(gc, "Error sending nickname");
            return;
        }
        g_free(buf);
        buf = pecker_format(pecker, "vvvv:", "USER", login_name, hostname, pecker->server, bioline);

        purple_debug_misc("pecker", "Command: %s\n", buf);
        if(pecker_send(pecker, buf) < 0)
        {
            purple_connection_error(gc, "Error registering with server");
            return;
        }
        g_free(login_name);
        g_free(buf);

        const char *server = purple_account_get_string(gc->account, "server",
                                                       IRC_DEFAULT_SERVER);
        char *url = g_strdup_printf("http://%s/messenger/config.txt?%s", server,
                                    pecker->chat_key);

        purple_util_fetch_url(url, FALSE, PECKER_USER_AGENT, FALSE, pecker_get_configtxt_cb, pecker);

        g_free(url);
        gc->inpa = purple_input_add(pecker->fd, PURPLE_INPUT_READ, pecker_input_cb, gc);

        connect_signals(gc);

    }
}

void
kill_hammer(gpointer * room, struct hammer_cb_data *data, gpointer * null)
{
    hammer_cb_data_destroy(data);
}

static void
pecker_close(PurpleConnection * gc)
{
    struct pecker_conn *pecker = gc->proto_data;

    purple_debug_misc("pecker", "pecker_close function has been called\n");
    if(pecker == NULL)
        return;

    pecker_cmd_quit(pecker, "quit", NULL, NULL);

    if(gc->inpa)
        purple_input_remove(gc->inpa);

    g_free(pecker->inbuf);
    purple_debug_misc("pecker", "closing fd %i\n", pecker->fd);
    close(pecker->fd);

    if(pecker->timer)
        purple_timeout_remove(pecker->timer);

    if(pecker->thumbnail)
        g_free(pecker->thumbnail);

    if(pecker->chat_key)
        g_free(pecker->chat_key);

    if(pecker->server_bioline)
        g_free(pecker->server_bioline);

    if(pecker->server_stats)
        g_free(pecker->server_stats);

    if(pecker->roomlist_filter)
        g_free(pecker->roomlist_filter);

    if(pecker->bio)
        g_free(pecker->bio);


    // Would we need to free each element, too?
    g_queue_free(pecker->namelists);

    g_hash_table_destroy(pecker->cmds);
    g_hash_table_destroy(pecker->msgs);
    g_hash_table_destroy(pecker->info_window_needed);
    g_hash_table_destroy(pecker->entry_order);
    if(pecker->motd)
        g_string_free(pecker->motd, TRUE);

    if(pecker->names)
        g_string_free(pecker->names, TRUE);

    if(pecker->nameconv)
        g_free(pecker->nameconv);
    if(pecker->subroom)
        g_free(pecker->subroom);

    g_hash_table_destroy(pecker->confighash);

    g_hash_table_foreach(pecker->hammers, (GHFunc) kill_hammer, NULL);

    g_free(pecker->server);
    g_free(pecker);
}

static int
pecker_im_send(PurpleConnection * gc, const char *who, const char *what, PurpleMessageFlags flags)
{
    struct pecker_conn *pecker = gc->proto_data;
    const char *args[2];
    char *automsg = NULL;
    char *stripped_msg = NULL;

    if(strchr(status_chars, *who) != NULL)
    {
        args[0] = who + 1;
    }
    else
    {
        args[0] = who;
    }
    stripped_msg = purple_markup_strip_html(what);
    if(flags & PURPLE_MESSAGE_AUTO_RESP)
    {
        automsg = g_strdup_printf("<AUTO-REPLY> %s", stripped_msg);
        g_free(stripped_msg);
        stripped_msg = NULL;
        args[1] = automsg;

    }
    else
    {
        args[1] = stripped_msg;
    }
    pecker_cmd_privmsg(pecker, "msg", NULL, args);
    if(automsg)
    {
        g_free(automsg);
    }
    if(stripped_msg)
        g_free(stripped_msg);

    return 1;
}

static void
pecker_get_info_quietly(PurpleConnection * gc, const char *who)
{
    purple_debug_misc("pecker", "request info quietly\n");
    struct pecker_conn *pecker = gc->proto_data;
    const char *args[1];

    args[0] = who;

    pecker_cmd_whois(pecker, "whois", NULL, args);
}


void
pecker_get_info(PurpleConnection * gc, const char *who)
{
    struct pecker_conn *pecker = gc->proto_data;

    const char *args[1];

    args[0] = who;

    char *normalized = g_strdup(purple_normalize(gc->account, who));

    purple_debug_misc("get_info", "who: %s; normalized: %s\n", who, normalized);
    struct get_info_data *info_data = g_new0(struct get_info_data, 1);

    info_data->pecker = pecker;
    info_data->who = normalized;
    info_data->gc = gc;
    info_data->info = NULL;
    PurpleNotifyUserInfo *info = purple_notify_user_info_new();

    purple_notify_user_info_add_pair(info, "Fetching info for", who);
    purple_notify_userinfo(gc, who, info, NULL, NULL);
    purple_debug_misc("get_info", "who: %s; normalized: %s\n", who, normalized);
    g_hash_table_insert(pecker->info_window_needed, normalized, info_data);
    purple_debug_misc("get_info", "who: %s; normalized: %s\n", who, normalized);
    // dunno wtf this is all about.
    //void *needed = g_hash_table_lookup(pecker->info_window_needed, normalized);

    //purple_debug_misc("pecker", "get info: Needed for %s? %x\n", normalized, needed);

    purple_notify_user_info_destroy(info);
    pecker_cmd_whois(pecker, "whois", NULL, args);
}

static void
pecker_set_status(PurpleAccount * account, PurpleStatus * status)
{
    // char *bio = NULL;
    // char *tmpmsg = NULL;
    // int i = 0;
    // struct pecker_conn *pecker = account->gc->proto_data;

    /* Set the away message */

    /**
     * In addition to setting the away message, set the Bio to the
     * away message; if the away message is NULL, then set the Bio
     * to the original bio.
     */

    /**
     *  The following would be great, and gay.com's server supports
     *  it, but gay.com's clients don't see the result.  So even though
     *  we can see the result, we won't bother.
     *
     * args[0] = msg;
     * pecker_cmd_away(pecker, "away", NULL, args);
     */
}

PeckerBuddy *
pecker_get_channel_member_reference(struct pecker_conn *pecker, const gchar * name)
{

    PeckerBuddy *channel_member = (PeckerBuddy *) g_hash_table_lookup(pecker->channel_members,
                                                                      name);

    if(!channel_member)
    {
        PeckerBuddy *channel_member = g_new0(PeckerBuddy, 1);

        channel_member->ref_count = 1;
        g_hash_table_insert(pecker->channel_members, g_strdup(purple_normalize(pecker->account, name)), channel_member);
        purple_debug_misc("pecker", "Creating channel_members entry for %s\n", name);
        return g_hash_table_lookup(pecker->channel_members, purple_normalize(pecker->account, name));
    }
    else
    {
        purple_debug_misc("pecker", "Adding reference to channel_members entry for %s\n", name);
        (channel_member->ref_count)++;
        return channel_member;
    }

}

gboolean
pecker_unreference_channel_member(struct pecker_conn * pecker, gchar * name)
{

    PeckerBuddy *channel_member;

    channel_member = (PeckerBuddy *) g_hash_table_lookup(pecker->channel_members, purple_normalize(pecker->account, name));
    if(!channel_member)
        return FALSE;
    else
    {

        if(channel_member->ref_count <= 0)
            purple_debug_error("pecker", "****Reference counting error with channel members struct.\n");

        channel_member->ref_count--;

        if(channel_member->ref_count == 0)
        {
            purple_debug_misc("pecker", "Removing %s from channel_members\n", name);
            return g_hash_table_remove(pecker->channel_members, purple_normalize(pecker->account, name));
        }
        return FALSE;
    }
}

static void
pecker_add_buddy(PurpleConnection * gc, PurpleBuddy * buddy, PurpleGroup * group)
{
    if(buddy->name)
    {
        buddy->name = g_strstrip(buddy->name);
    }
    if(buddy->alias)
    {
        buddy->alias = g_strstrip(buddy->alias);
    }
    if(buddy->server_alias)
    {
        buddy->server_alias = g_strstrip(buddy->server_alias);
    }
    struct pecker_conn *pecker = (struct pecker_conn *) gc->proto_data;
    struct pecker_buddy *ib = g_new0(struct pecker_buddy, 1);

    ib->name = g_strdup(buddy->name);
    ib->done = FALSE;
    ib->online = FALSE;
    ib->bio = NULL;
    ib->thumbnail = NULL;
    ib->sex = NULL;
    ib->age = NULL;
    ib->location = NULL;
    g_hash_table_replace(pecker->buddies, ib->name, ib);
    purple_debug_misc("pecker", "Add buddy: %s\n", buddy->name);

    /*
     * if the timer isn't set, this is during signon, so we don't want to
     * flood ourself off with WHOIS's, so we don't, but after that we want
     * to know when someone's online asap
     */
    if(pecker->timer)
        pecker_whois_one(pecker, ib);
}

static void
pecker_remove_buddy(PurpleConnection * gc, PurpleBuddy * buddy, PurpleGroup * group)
{
    struct pecker_conn *pecker = (struct pecker_conn *) gc->proto_data;

    /*
     * Only remove buddy->name from pecker->buddies if it doesn't
     * exist in any other group on the buddy list.  This allows us
     * to manage the buddy once, even though it might exist in
     * several groups within the buddy list.
     *
     * To add to confusion, the buddy being deleted is not yet deleted,
     * so we look for less than two identical buddies, and if so, then
     * remove the buddy from pecker->buddies.
     */

    GSList *buddies = purple_find_buddies(pecker->account, buddy->name);
    guint length = g_slist_length(buddies);

    if(length < 2)
    {
        g_hash_table_remove(pecker->buddies, buddy->name);
    }

    g_slist_free(buddies);
}

static void
pecker_input_cb(gpointer data, gint source, PurpleInputCondition cond)
{
    PurpleConnection *gc = data;
    struct pecker_conn *pecker = gc->proto_data;
    char *cur, *end;
    int len;

    if(pecker->inbuflen < pecker->inbufused + IRC_INITIAL_BUFSIZE)
    {
        pecker->inbuflen += IRC_INITIAL_BUFSIZE;
        pecker->inbuf = g_realloc(pecker->inbuf, pecker->inbuflen);
    }

    if((len = read(pecker->fd, pecker->inbuf + pecker->inbufused, IRC_INITIAL_BUFSIZE - 1)) < 0)
    {
        purple_connection_error(gc, "Read error");
        return;
    }
    else if(len == 0)
    {
        purple_connection_error(gc, "Server has disconnected");
        return;
    }

    pecker->inbufused += len;
    pecker->inbuf[pecker->inbufused] = '\0';

    cur = pecker->inbuf;
    while (cur < pecker->inbuf + pecker->inbufused && ((end = strstr(cur, "\r\n")) || (end = strstr(cur, "\n"))))
    {
        int step = (*end == '\r' ? 2 : 1);

        *end = '\0';
        pecker_parse_msg(pecker, cur);
        cur = end + step;
    }
    if(cur != pecker->inbuf + pecker->inbufused)
    {                           /* leftover */
        pecker->inbufused -= (cur - pecker->inbuf);
        memmove(pecker->inbuf, cur, pecker->inbufused);
    }
    else
    {
        pecker->inbufused = 0;
    }
}

static void
pecker_add_permit(PurpleConnection * gc, const char *name)
{
    if(!pecker_nick_check(name))
    {
        purple_privacy_permit_remove(gc->account, name, TRUE);
        purple_notify_error(gc, "Invalid User Name", name, "Invalid user name not added.");
    }
    else
    {
        pecker_privacy_change(gc, name);
    }
}

static void
pecker_add_deny(PurpleConnection * gc, const char *name)
{
    if(!pecker_nick_check(name))
    {
        purple_privacy_deny_remove(gc->account, name, TRUE);
        purple_notify_error(gc, "Invalid User Name", name, "Invalid user name not added.");
        return;
    }
    pecker_server_store_deny(gc, name, TRUE);
    pecker_privacy_change(gc, name);
}

static void
pecker_rem_permit(PurpleConnection * gc, const char *name)
{
    pecker_privacy_change(gc, name);
}

static void
pecker_rem_deny(PurpleConnection * gc, const char *name)
{
    pecker_server_store_deny(gc, name, FALSE);
    pecker_privacy_change(gc, name);
}

static void
pecker_set_permit_deny(PurpleConnection * gc)
{
    pecker_privacy_change(gc, NULL);
}

/**
static void
pecker_warn(PurpleConnection * gc, const char *who, gboolean anonymous)
{
        void *handle = NULL;
        struct pecker_conn *pecker = gc->proto_data;
        char *buf =
                g_strdup_printf("http://%s/members/report/form.html?area=chat&room=&report=%s",
                                pecker->server, who);
        purple_notify_uri(handle, buf);
        g_free(buf);
}
*/

static void
pecker_chat_join(PurpleConnection * gc, GHashTable * data)
{
    struct pecker_conn *pecker = gc->proto_data;
    const char *args[1];
    char *alias = NULL;

    PurpleChat *c = NULL;

    /*
     * need a copy, because data gets
     * destroyed in roomlist.c
     */
    GHashTable *chatinfo = NULL;

    args[0] = g_hash_table_lookup(data, "channel");

    if(args[0])
    {
        alias = g_hash_table_lookup(data, "description");
        c = purple_blist_find_chat(purple_connection_get_account(gc), args[0]);
        if(!c)
        {
            chatinfo = g_hash_table_new(g_str_hash, g_str_equal);

            g_hash_table_replace(chatinfo, "channel", g_strdup(args[0]));

            c = purple_chat_new(purple_connection_get_account(gc), alias, chatinfo);

            purple_blist_add_chat(c, NULL, NULL);
        }
    }

    if(!args[0] || *args[0] != '#')
    {
        /* Trigger a room search in config.txt....  */
        return;
    }

    pecker_cmd_join(pecker, "join", NULL, args);
}

static char *
pecker_get_chat_name(GHashTable * data)
{
    return g_strdup(g_hash_table_lookup(data, "channel"));
}

static void
pecker_chat_invite(PurpleConnection * gc, int id, const char *message, const char *name)
{
    struct pecker_conn *pecker = gc->proto_data;
    PurpleConversation *convo = purple_find_chat(gc, id);
    const char *args[2];

    if(!convo)
    {
        purple_debug(PURPLE_DEBUG_ERROR, "pecker", "Got chat invite request for bogus chat\n");
        return;
    }
    args[0] = name;
    args[1] = purple_conversation_get_name(convo);
    pecker_cmd_invite(pecker, "invite", purple_conversation_get_name(convo), args);
}

static void
pecker_chat_leave(PurpleConnection * gc, int id)
{
    struct pecker_conn *pecker = gc->proto_data;
    PurpleConversation *convo = purple_find_chat(gc, id);
    const char *args[2];

    if(!convo)
        return;

    args[0] = purple_conversation_get_name(convo);
    args[1] = NULL;
    pecker_cmd_part(pecker, "part", purple_conversation_get_name(convo), args);
    serv_got_chat_left(gc, id);
}

static int
pecker_chat_send(PurpleConnection * gc, int id, const char *what, PurpleMessageFlags flags)
{
    struct pecker_conn *pecker = gc->proto_data;
    PurpleConversation *convo = purple_find_chat(gc, id);
    const char *args[2];
    char *tmp;

    if(!convo)
    {
        purple_debug(PURPLE_DEBUG_ERROR, "pecker", "chat send on nonexistent chat\n");
        return -EINVAL;
    }
#if 0
    if(*what == '/')
    {
        return pecker_parse_cmd(pecker, convo->name, what + 1);
    }
#endif
    args[0] = convo->name;
    args[1] = what;

    pecker_cmd_privmsg(pecker, "msg", NULL, args);

    tmp = g_markup_escape_text(what, -1);
    serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), 0, tmp, time(NULL));
    g_free(tmp);
    return 0;
}

static guint
pecker_nick_hash(const char *nick)
{
    char *lc = NULL;
    guint bucket;

    if(!nick)
        return 0;
    lc = g_utf8_strdown(nick, -1);
    bucket = g_str_hash(lc);
    g_free(lc);

    return bucket;
}

static gboolean
pecker_nick_equal(const char *nick1, const char *nick2)
{
    return (purple_utf8_strcasecmp(nick1, nick2) == 0);
}

static void
pecker_channel_member_free(PeckerBuddy * cm)
{
    g_free(cm->name);
    g_free(cm->bio);
    g_free(cm->thumbnail);
    g_free(cm->sex);
    g_free(cm->age);
    g_free(cm->location);
    g_free(cm);
}

static void
pecker_buddy_free(struct pecker_buddy *ib)
{
    g_free(ib->name);
    g_free(ib->bio);
    g_free(ib->thumbnail);
    g_free(ib->sex);
    g_free(ib->age);
    g_free(ib->location);
    g_free(ib);
}

static PurpleChat *
pecker_find_blist_chat(PurpleAccount * account, const char *name)
{
    char *chat_name;
    PurpleChat *chat;
    PurplePlugin *prpl;
    PurplePluginProtocolInfo *prpl_info = NULL;
    struct proto_chat_entry *pce;
    PurpleBlistNode *node, *group;
    GList *parts;

    PurpleBuddyList *purplebuddylist = purple_get_blist();

    g_return_val_if_fail(purplebuddylist != NULL, NULL);
    g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL);

    if(!purple_account_is_connected(account))
        return NULL;

    prpl = purple_find_prpl(purple_account_get_protocol_id(account));
    prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);

    for (group = purplebuddylist->root; group != NULL; group = group->next)
    {
        for (node = group->child; node != NULL; node = node->next)
        {
            if(PURPLE_BLIST_NODE_IS_CHAT(node))
            {

                chat = (PurpleChat *) node;

                if(account != chat->account)
                    continue;

                parts = prpl_info->chat_info(purple_account_get_connection(chat->account));

                pce = parts->data;
                chat_name = g_hash_table_lookup(chat->components, pce->identifier);

                if(chat->account == account && chat_name != NULL && name != NULL && g_pattern_match_simple(chat_name, name))
                {
                    return chat;
                }
            }
        }
    }

    return NULL;
}

static PurpleRoomlist *
pecker_roomlist_get_list(PurpleConnection * gc)
{
    struct pecker_conn *pecker;
    GList *fields = NULL;
    PurpleRoomlistField *f;
    char *buf;

    pecker = gc->proto_data;

    if(pecker->roomlist)
    {
        purple_roomlist_unref(pecker->roomlist);
    }

    pecker->roomlist = purple_roomlist_new(purple_connection_get_account(gc));

    f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "Channel", "channel", FALSE);
    fields = g_list_prepend(fields, f);

    f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", "description", TRUE);
    fields = g_list_prepend(fields, f);

    purple_roomlist_set_fields(pecker->roomlist, fields);

    /**
     * Member created rooms are retrieved through the IRC protocol
     * and after the last response is recieved from that request
     * the static rooms are added
     */

    buf = pecker_format(pecker, "v", "LIST #_*");
    pecker_send(pecker, buf);
    g_free(buf);

    return pecker->roomlist;
}

static void
pecker_roomlist_cancel(struct _PurpleRoomlist *list)
{
    PurpleConnection *gc = purple_account_get_connection(list->account);
    struct pecker_conn *pecker;

    if(gc == NULL)
        return;

    pecker = gc->proto_data;

    purple_roomlist_set_in_progress(list, FALSE);

    if(pecker->roomlist == list)
    {
        g_list_free(g_list_nth_data(list->rooms, 0));
        pecker->roomlist = NULL;
        purple_roomlist_unref(list);
    }

    if(pecker->roomlist_filter)
    {
        g_free(pecker->roomlist_filter);
        pecker->roomlist_filter = NULL;
    }
}

void
pecker_roomlist_expand_category(struct _PurpleRoomlist *list, struct _PurpleRoomlistRoom *category)
{
    PurpleRoomlistRoom *room = NULL;
    gchar *altname = NULL;
    gchar *altchan = NULL;
    int i = 0;

    if(category->type & PURPLE_ROOMLIST_ROOMTYPE_ROOM && !category->expanded_once)
    {

        category->expanded_once = TRUE;

        int max = purple_prefs_get_int("/plugins/prpl/pecker/chat_room_instances");

        gchar *name = category->fields->data;
        gchar *chan = category->fields->next->data;

        for (i = 1; i <= max; i++)
        {
            altname = g_strdup_printf("%.*s%d", ((unsigned int) strlen(name) - 1), name, i);
            altchan = g_strdup_printf("%.*s%d", ((unsigned int) strlen(chan) - 1), chan, i);

            room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, altname, category);

            purple_roomlist_room_add_field(list, room, altname);
            purple_roomlist_room_add_field(list, room, altchan);
            purple_roomlist_room_add(list, room);
            g_free(altname);
            g_free(altchan);
        }
    }
    purple_roomlist_set_in_progress(list, FALSE);
}

static PurplePluginProtocolInfo prpl_info = {
    0,                          /* options */
    NULL,                       /* user_splits */
    NULL,                       /* protocol_options */
    {"jpg,jpeg,gif,bmp,ico", 57, 77, 57, 77, PURPLE_ICON_SCALE_DISPLAY},        /* icon_spec */
    pecker_blist_icon,          /* list_icon */
    NULL,                       /* list_emblems */
    pecker_status_text,         /* status_text */
    pecker_tooltip_text,        /* tooltip_text */
    pecker_status_types,        /* status_types */
    pecker_blist_node_menu,     /* blist_node_menu */
    pecker_chat_join_info,      /* chat_info */
    pecker_chat_info_defaults,  /* chat_info_defaults */
    pecker_login,               /* login */
    pecker_close,               /* close */
    pecker_im_send,             /* send_im */
    pecker_set_info,            /* set_info */
    NULL,                       /* send_typing */
    pecker_get_info,            /* get_info */
    pecker_set_status,          /* set_status */
    NULL,                       /* set_idle */
    NULL,                       /* change_passwd */
    pecker_add_buddy,           /* add_buddy */
    NULL,                       /* add_buddies */
    pecker_remove_buddy,        /* remove_buddy */
    NULL,                       /* remove_buddies */
    pecker_add_permit,          /* add_permit */
    pecker_add_deny,            /* add_deny */
    pecker_rem_permit,          /* rem_permit */
    pecker_rem_deny,            /* rem_deny */
    pecker_set_permit_deny,     /* set_permit_deny */
    pecker_chat_join,           /* join_chat */
    NULL,                       /* reject_chat */
    pecker_get_chat_name,       /* get_chat_name */
    pecker_chat_invite,         /* chat_invite */
    pecker_chat_leave,          /* chat_leave */
    NULL,                       /* chat_whisper */
    pecker_chat_send,           /* chat_send */
    NULL,                       /* keepalive */
    NULL,                       /* register_user */
    NULL,                       /* get_cb_info */
    NULL,                       /* get_cb_away */
    NULL,                       /* alias_buddy */
    NULL,                       /* group_buddy */
    NULL,                       /* rename_group */
    NULL,                       /* buddy_free */
    NULL,                       /* convo_closed */
    NULL,                       /* normalize */
    NULL,                       /* set_buddy_icon */
    NULL,                       /* remove_group */
    NULL,                       /* get_cb_real_name */
    NULL,                       /* set_chat_topic */
    pecker_find_blist_chat,     /* find_blist_chat */
    pecker_roomlist_get_list,   /* roomlist_get_list */
    pecker_roomlist_cancel,     /* roomlist_cancel */
    pecker_roomlist_expand_category,    /* roomlist_expand_category */
    NULL,                       /* can_receive_file */
    NULL,                       /* send_file */
    NULL,                       /* new_xfr */
    NULL,                       /* offline_mode */
    NULL,                       /* whiteboard_prpl_ops */
    NULL,                       /* send_raw */
};

void
deref_one_user(gpointer * user, gpointer * data)
{

    struct pecker_conn *pecker = (struct pecker_conn *) data;
    PurpleConvChatBuddy *cb = (PurpleConvChatBuddy *) user;

    purple_debug_misc("pecker", "Removing %s in %s from list\n", (char *) cb->name, (const char *) cb);
    purple_debug_misc("      ", "Success was: %i\n", pecker_unreference_channel_member(pecker, cb->name));

}
static void
pecker_clean_channel_members(PurpleConversation * conv)
{
    if(strncmp(conv->account->protocol_id, "prpl-pecker", 9))
        return;

    g_return_if_fail(conv != NULL);

    if(conv->type == PURPLE_CONV_TYPE_CHAT)
    {
        PurpleConvChat *chat = purple_conversation_get_chat_data(conv);
        PurpleConnection *gc = purple_conversation_get_gc(conv);

        g_return_if_fail(gc != NULL);
        struct pecker_conn *pecker = gc->proto_data;
        GList *users = purple_conv_chat_get_users(chat);

        purple_debug_misc("pecker", "got userlist %s length %i\n", ((char *) users), g_list_length(users));
        g_list_foreach(users, (GFunc) deref_one_user, pecker);
    }
    else if(conv->type == PURPLE_CONV_TYPE_IM)
    {
        purple_debug_misc("pecker", "removing reference to %s\n", conv->name);
        PurpleConnection *gc = purple_conversation_get_gc(conv);

        g_return_if_fail(gc != NULL);
        struct pecker_conn *pecker = gc->proto_data;

        pecker_unreference_channel_member(pecker, conv->name);
    }
}
static void
pecker_get_photo_info(PurpleConversation * conv)
{
    char *buf;
    char *name;

    purple_debug_misc("pecker", "* Got conversation-created signal\n");
    if(strncmp(conv->account->protocol_id, "prpl-pecker", 9) == 0 && purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
    {

        /**
         * First check to see if we already have the photo via
         * the buddy list process.
         */

        struct pecker_conn *pecker;

        PurpleConnection *gc = purple_conversation_get_gc(conv);

        pecker = (struct pecker_conn *) gc->proto_data;

        if(!pecker)
        {
            return;
        }

        struct pecker_buddy *ib = g_hash_table_lookup(pecker->buddies, conv->name);

        if(ib)
        {
            return;
        }

        /**
         * Since this person isn't in our buddy list, go ahead
         * with the WHOIS to get the photo for the IM thumbnail
         */


        name = pecker_nick_to_gcom_strdup(conv->name);
        buf = pecker_format(pecker, "vn", "WHOIS", name);
        purple_debug_misc("pecker", "Conversation triggered command: %s\n", buf);
        pecker_send(pecker, buf);
        pecker_get_channel_member_reference(pecker, name);
        g_free(name);
        g_free(buf);
        // Opens a reference in channel_members.

    }
}

/* never used ?? */
static PurplePluginPrefFrame *
get_plugin_pref_frame(PurplePlugin * plugin)
{
    PurplePluginPrefFrame *frame;
    PurplePluginPref *ppref;

    frame = purple_plugin_pref_frame_new();

    ppref = purple_plugin_pref_new_with_label("Chat Rooms");
    purple_plugin_pref_frame_add(frame, ppref);

    ppref = purple_plugin_pref_new_with_name_and_label("/plugins/prpl/pecker/show_join", "Show entrance announcement");
    purple_plugin_pref_frame_add(frame, ppref);

    ppref =
        purple_plugin_pref_new_with_name_and_label("/plugins/prpl/pecker/show_bio_with_join",
                                                   "Show member bio with entrance announcement");
    purple_plugin_pref_frame_add(frame, ppref);

    ppref = purple_plugin_pref_new_with_name_and_label("/plugins/prpl/pecker/show_part", "Show exit announcement");
    purple_plugin_pref_frame_add(frame, ppref);

    ppref =
        purple_plugin_pref_new_with_name_and_label("/plugins/prpl/pecker/chat_room_instances",
                                                   "Number of chat room instances to display");
    purple_plugin_pref_set_bounds(ppref, 0, 9);
    purple_plugin_pref_frame_add(frame, ppref);

    ppref = purple_plugin_pref_new_with_label("Bio-Based Chat Room Activity Filtering");
    purple_plugin_pref_frame_add(frame, ppref);

    return frame;
}


/* never used ? */
/**
static PurplePluginUiInfo prefs_info = {
    get_plugin_pref_frame
};
*/

static PurplePluginInfo info = {
    PURPLE_PLUGIN_MAGIC,
    PURPLE_MAJOR_VERSION,
    PURPLE_MINOR_VERSION,
    PURPLE_PLUGIN_PROTOCOL,                            /**< type           */
    NULL,                                              /**< ui_requirement */
    0,                                                 /**< flags          */
    NULL,                                              /**< dependencies   */
    PURPLE_PRIORITY_DEFAULT,                           /**< priority       */

    "prpl-pecker",                                     /**< id             */
    "Gay.com",                                         /**< name           */
    VERSION,                                           /**< version        */
    "Gay.com Protocol Plugin",                         /**  summary        */
    "Gay.com Messaging based on IRC",                  /**  description    */
    NULL,                                              /**< author         */
    "http://pidgin-woodpecker.googlecode.com",         /**< homepage       */

    NULL,                                              /**< load           */
    NULL,                                              /**< unload         */
    NULL,                                              /**< destroy        */
    NULL,                                              /**< ui_info        */
    &prpl_info,                                        /**< extra_info     */
    NULL,
    pecker_actions
};


void
pecker_get_room_namelist(PurpleAccount * account, const char *room)
{


    if(!account || !room)
        return;

    const char *args[1] = { room };
    struct pecker_conn *pecker = (struct pecker_conn *) account->gc->proto_data;
    PeckerNamelist *namelist = g_new0(PeckerNamelist, 1);

    namelist->roomname = g_strdup(room);
    if(g_str_has_suffix(room, "*"))
        namelist->multi_room = TRUE;
    else
        namelist->multi_room = FALSE;
    namelist->members = NULL;
    namelist->num_rooms = 100;
    namelist->current = 0;

    g_queue_push_tail(pecker->namelists, namelist);
    // g_hash_table_insert(pecker->namelists, g_strdup(room), namelist); 

    // g_hash_table_insert(pecker->namelist_pending, list);
    pecker_cmd_who(pecker, NULL, NULL, args);
}



static void
connect_signals(PurpleConnection * plugin)
{

    static gboolean connection_done = FALSE;

    if(connection_done)
        return;
    connection_done = TRUE;
    purple_debug_misc("pecker", "CONNECTING SIGNALS: purple_conversations_get_handle(): %s\n",
                      ((const char *) purple_conversations_get_handle()));
    purple_signal_connect(purple_conversations_get_handle(), "conversation-created", plugin,
                          PURPLE_CALLBACK(pecker_get_photo_info), NULL);

    purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation", plugin,
                          PURPLE_CALLBACK(pecker_clean_channel_members), NULL);


    purple_signal_connect(purple_accounts_get_handle(), "request-namelist", plugin, PURPLE_CALLBACK(pecker_get_room_namelist),
                          NULL);
    purple_signal_connect(purple_accounts_get_handle(), "request-info-quietly", plugin, PURPLE_CALLBACK(pecker_get_info_quietly),
                          NULL);



}
static void
init_plugin(PurplePlugin * plugin)
{

    PurpleAccountOption *option;

    option = purple_account_option_string_new("Bio Line", "bioline", "");
    prpl_info.protocol_options = g_list_prepend(prpl_info.protocol_options, option);

    option = purple_account_option_int_new("Port", "port", IRC_DEFAULT_PORT);
    prpl_info.protocol_options = g_list_prepend(prpl_info.protocol_options, option);

    option = purple_account_option_string_new("Server", "server", IRC_DEFAULT_SERVER);
    prpl_info.protocol_options = g_list_prepend(prpl_info.protocol_options, option);

    purple_signal_register(purple_accounts_get_handle(), "info-updated", purple_marshal_VOID__POINTER_POINTER, NULL, 2,
                           purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT), purple_value_new(PURPLE_TYPE_POINTER,
                                                                                                           PURPLE_TYPE_CHAR));

    purple_signal_register(purple_accounts_get_handle(), "namelist-complete", purple_marshal_VOID__POINTER_POINTER, NULL, 2,
                           purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT), purple_value_new(PURPLE_TYPE_POINTER));
    purple_signal_register(purple_accounts_get_handle(), "request-namelist", purple_marshal_VOID__POINTER_POINTER, NULL, 2,
                           purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT), purple_value_new(PURPLE_TYPE_POINTER,
                                                                                                           PURPLE_TYPE_CHAR));
    purple_signal_register(purple_accounts_get_handle(), "request-info-quietly", purple_marshal_VOID__POINTER_POINTER, NULL, 2,
                           purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT), purple_value_new(PURPLE_TYPE_POINTER,
                                                                                                           PURPLE_TYPE_CHAR));
    purple_prefs_add_none("/plugins/prpl/pecker");
    purple_prefs_add_int("/plugins/prpl/pecker/chat_room_instances", 4);
    purple_prefs_add_bool("/plugins/prpl/pecker/show_join", TRUE);
    purple_prefs_add_bool("/plugins/prpl/pecker/show_part", TRUE);
    purple_prefs_add_bool("/plugins/prpl/pecker/show_bio_with_join", TRUE);

    _pecker_plugin = plugin;

    pecker_register_commands();
}

PURPLE_INIT_PLUGIN(pecker, init_plugin, info);



/**
 * vim:tabstop=4:shiftwidth=4:expandtab:
 */
